
#ifndef _MEMBER_BSPLINECURVE_H_
#define _MEMBER_BSPLINECURVE_H_

#include <functional>
#include <algorithm>
#include <BPair>
#include <BSplineCurve>

#define spline_member(spline)		((member_BSplineCurve*)((BSplineCurve*)spline)->_ptr)

namespace BWE
{
	struct Segment;


	struct Segment
	{
		BReal ax = 0, bx = 0, cx = 0, dx = 0;
		BReal ay = 0, by = 0, cy = 0, dy = 0;
		BReal az = 0, bz = 0, cz = 0, dz = 0;

		BReal begin = 0;
		BReal length = 0;
		BVector coeff = 0;
		BVectorArray vertices;

		inline BReal speed(BReal u) const
		{
			BReal x = bx + 2 * cx * u + 3 * dx * u * u;
			BReal y = by + 2 * cy * u + 3 * dy * u * u;
			BReal z = bz + 2 * cz * u + 3 * dz * u * u;
			return std::sqrt(x * x + y * y + z * z);
		}

		inline BReal adaptiveSimpson(BReal a, BReal b, BReal eps)
		{
			const BReal c = (a + b) / 2;
			const BReal h = b - a;
			const BReal fa = speed(a);
			const BReal fc = speed(c);
			const BReal fb = speed(b);
			const BReal S = h * (fa + 4 * fc + fb) / 6;

			return adaptiveRecursive(a, b, eps, S, fa, fb, fc);
		}

		inline  BReal adaptiveRecursive(BReal a, BReal b, BReal eps, BReal S, BReal fa, BReal fb, BReal fc)
		{
			const BReal c = (a + b) / 2;
			const BReal h = (b - a) / 2;
			const BReal d = (a + c) / 2;
			const BReal e = (c + b) / 2;
			const BReal fd = speed(d);
			const BReal fe = speed(e);

			const BReal S_left = h * (fa + 4 * fd + fc) / 6;
			const BReal S_right = h * (fc + 4 * fe + fb) / 6;
			const BReal S2 = S_left + S_right;

			if (std::abs(S2 - S) <= 15 * eps) {
				return S2 + (S2 - S) / 15;
			}
			return adaptiveRecursive(a, c, eps / 2, S_left, fa, fc, fd) +
				adaptiveRecursive(c, b, eps / 2, S_right, fc, fb, fe);
		}

		inline BVector vertex(BReal t) const
		{
			BReal x = ax + t * (bx + t * (cx + t * dx));
			BReal y = ay + t * (by + t * (cy + t * dy));
			BReal z = az + t * (bz + t * (cz + t * dz));
			return BVector(x, y, z);
		}

		inline BVector direct(BReal t) const
		{
			BReal x = bx + 2 * cx * t + 3 * dx * t * t;
			BReal y = by + 2 * cy * t + 3 * dy * t * t;
			BReal z = bz + 2 * cz * t + 3 * dz * t * t;
			return BVector(x, y, z);
		}
		inline BVector secondDirect(BReal t) const
		{
			BReal x = 2 * cx + 6 * dx * t;
			BReal y = 2 * cy + 6 * dy * t;
			BReal z = 2 * cz + 6 * dz * t;
			return BVector(x, y, z);
		}

		inline void freshPoints(int num)
		{
			BReal tolerance = sqrt(REAL_EPSILON);
			vertices.clear();
			vertices.reserve(num);
			BReal s = BReal(1) / num;
			for (int i = 0; i < num; i++)
			{
				BReal t = s * i;
				BVector p = this->vertex(t);
				if (i > 2)
				{
					BVector v1 = p - vertices[vertices.size() - 1];
					BVector v2 = p - vertices[vertices.size() - 2];
					BReal sumLen2 = v1.length2() * v2.length2();
					if (sumLen2 < REAL_EPSILON)
						continue;
					BReal clen2 = v1.cross(v2).length2();
					if ((clen2 / sumLen2) <= tolerance)
						continue;
				}
				vertices.append(p);
			}
		}
	};

	class member_BSplineCurve
	{
		friend class BSplineCurve;
	public:
		member_BSplineCurve(BSplineCurve* stripe);
		~member_BSplineCurve();

		BArray<BReal> computeCoefficients(const BArray<BReal>& values);

		BSplineCurve*		boss;
		BReal				thickness;
		bool				closeLoop;

		bool				dirty;
		BReal				length;
		BVectorArrayHolder	vertices;
		BArray<Segment>		segments;

	};
}

#endif